From 13e22e20300b7312e52bba7d077fc7e231695fc1 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Wed, 25 Feb 2015 20:34:12 +0100 Subject: [PATCH] gtkwindow: Move window dragging to a standalone drag gesture The gesture is hooked to the capture phase, so it works for buttons in header bars and whatnot. In order to be friendly to the widget it is capturing events from, an ugly hack is in place to avoid capturing events when the target widget has a gesture that would consume motion events. --- gtk/gtkmain.c | 14 ++--- gtk/gtkwidget.c | 28 ++++++++++ gtk/gtkwidgetprivate.h | 2 + gtk/gtkwindow.c | 116 ++++++++++++++++++++++++++++------------- 4 files changed, 118 insertions(+), 42 deletions(-) diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 806dc6ddff..f50025e3c9 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -1567,12 +1567,6 @@ gtk_main_do_event (GdkEvent *event) event_widget = gtk_get_event_widget (event); } - if (GTK_IS_WINDOW (event_widget)) - { - if (_gtk_window_check_handle_wm_event (event)) - return; - } - window_group = gtk_main_get_window_group (event_widget); device = gdk_event_get_device (event); @@ -1583,6 +1577,14 @@ gtk_main_do_event (GdkEvent *event) if (!grab_widget) grab_widget = gtk_window_group_get_current_grab (window_group); + if (GTK_IS_WINDOW (event_widget) || + (grab_widget && grab_widget != event_widget && + !gtk_widget_is_ancestor (event_widget, grab_widget))) + { + if (_gtk_window_check_handle_wm_event (event)) + return; + } + /* Find out the topmost widget where captured event propagation * should start, which is the widget holding the GTK+ grab * if any, otherwise it's left NULL and events are emitted diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 7f132df0c0..cbe2e363f9 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -17280,3 +17280,31 @@ _gtk_widget_list_controllers (GtkWidget *widget, return retval; } + +gboolean +_gtk_widget_consumes_motion (GtkWidget *widget) +{ + EventControllerData *data; + GtkWidgetPrivate *priv; + GList *l; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + priv = widget->priv; + + for (l = priv->event_controllers; l; l = l->next) + { + data = l->data; + + if (data->controller == NULL) + continue; + + if (!GTK_IS_GESTURE_SINGLE (data->controller)) + return TRUE; + else if (GTK_IS_GESTURE_DRAG (data->controller) || + GTK_IS_GESTURE_SWIPE (data->controller)) + return TRUE; + } + + return FALSE; +} diff --git a/gtk/gtkwidgetprivate.h b/gtk/gtkwidgetprivate.h index 48c9f8cdf4..676d6e856c 100644 --- a/gtk/gtkwidgetprivate.h +++ b/gtk/gtkwidgetprivate.h @@ -160,6 +160,8 @@ void _gtk_widget_remove_controller (GtkWidget GtkEventController *controller); GList * _gtk_widget_list_controllers (GtkWidget *widget, GtkPropagationPhase phase); +gboolean _gtk_widget_consumes_motion (GtkWidget *widget); + gboolean gtk_widget_has_tick_callback (GtkWidget *widget); void gtk_widget_set_csd_input_shape (GtkWidget *widget, diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 54305d5a75..ab96261e17 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -233,11 +233,10 @@ struct _GtkWindowPrivate guint fullscreen : 1; guint tiled : 1; - guint drag_possible : 1; - guint use_subsurface : 1; GtkGesture *multipress_gesture; + GtkGesture *drag_gesture; GdkWindow *hardcoded_window; }; @@ -1435,8 +1434,10 @@ multipress_gesture_pressed_cb (GtkGestureMultiPress *gesture, if (!event) return; + if (n_press > 1) + gtk_gesture_set_state (priv->drag_gesture, GTK_EVENT_SEQUENCE_DENIED); + region = get_active_region_type (window, (GdkEventAny*) event, x, y); - priv->drag_possible = FALSE; if (button == GDK_BUTTON_SECONDARY && region == GTK_WINDOW_REGION_TITLE) { @@ -1476,8 +1477,6 @@ multipress_gesture_pressed_cb (GtkGestureMultiPress *gesture, case GTK_WINDOW_REGION_TITLE: if (n_press == 2) gtk_window_titlebar_action (window, event, button, n_press); - if (n_press == 1) - priv->drag_possible = TRUE; if (gtk_widget_has_grab (widget)) gtk_gesture_set_sequence_state (GTK_GESTURE (gesture), @@ -1502,41 +1501,71 @@ multipress_gesture_pressed_cb (GtkGestureMultiPress *gesture, } static void -multipress_gesture_stopped_cb (GtkGestureMultiPress *gesture, - GtkWindow *window) +drag_gesture_begin_cb (GtkGestureDrag *gesture, + gdouble x, + gdouble y, + GtkWindow *window) { GdkEventSequence *sequence; - GtkWindowPrivate *priv; + GtkWindowRegion region; const GdkEvent *event; - gdouble x, y; + GtkWidget *event_widget; - if (!gtk_gesture_is_active (GTK_GESTURE (gesture))) - return; - - /* The gesture is active, but stopped, so a too long - * press happened, or one drifting out of the threshold - */ - priv = gtk_window_get_instance_private (window); sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); - gtk_gesture_get_point (GTK_GESTURE (gesture), sequence, &x, &y); - if (priv->drag_possible) + if (!event) + return; + + event_widget = gtk_get_event_widget ((GdkEvent *) event); + + if (event_widget != GTK_WIDGET (window) && + !gtk_widget_has_grab (event_widget) && + _gtk_widget_consumes_motion (event_widget)) + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + else + { + region = get_active_region_type (window, (GdkEventAny*) event, x, y); + + if (region != GTK_WINDOW_REGION_TITLE) + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + } +} + +static void +drag_gesture_update_cb (GtkGestureDrag *gesture, + gdouble offset_x, + gdouble offset_y, + GtkWindow *window) +{ + gint double_click_distance; + GtkSettings *settings; + + settings = gtk_widget_get_settings (GTK_WIDGET (window)); + g_object_get (settings, + "gtk-double-click-distance", &double_click_distance, + NULL); + + if (ABS (offset_x) > double_click_distance || + ABS (offset_y) > double_click_distance) { - gdouble x_root, y_root; + gdouble start_x, start_y; + gint x_root, y_root; + + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + + gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y); + gdk_window_get_root_coords (gtk_widget_get_window (GTK_WIDGET (window)), + start_x, start_y, &x_root, &y_root); - gtk_gesture_set_sequence_state (GTK_GESTURE (gesture), - sequence, GTK_EVENT_SEQUENCE_CLAIMED); - gdk_event_get_root_coords (event, &x_root, &y_root); gdk_window_begin_move_drag_for_device (gtk_widget_get_window (GTK_WIDGET (window)), - gdk_event_get_device ((GdkEvent*) event), - GDK_BUTTON_PRIMARY, + gtk_gesture_get_device (GTK_GESTURE (gesture)), + gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)), x_root, y_root, - gdk_event_get_time (event)); - } + gtk_get_current_event_time ()); - gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture)); - priv->drag_possible = FALSE; + gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture)); + } } static void @@ -1632,8 +1661,14 @@ gtk_window_constructed (GObject *object) GTK_PHASE_NONE); g_signal_connect (priv->multipress_gesture, "pressed", G_CALLBACK (multipress_gesture_pressed_cb), object); - g_signal_connect (priv->multipress_gesture, "stopped", - G_CALLBACK (multipress_gesture_stopped_cb), object); + + priv->drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (object)); + gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (priv->drag_gesture), + GTK_PHASE_CAPTURE); + g_signal_connect (priv->drag_gesture, "drag-begin", + G_CALLBACK (drag_gesture_begin_cb), object); + g_signal_connect (priv->drag_gesture, "drag-update", + G_CALLBACK (drag_gesture_update_cb), object); } } @@ -7766,8 +7801,10 @@ get_active_region_type (GtkWindow *window, GdkEventAny *event, gint x, gint y) static gboolean gtk_window_handle_wm_event (GtkWindow *window, - GdkEvent *event) + GdkEvent *event, + gboolean run_drag) { + gboolean retval = GDK_EVENT_PROPAGATE; GtkWindowPrivate *priv; if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE || @@ -7776,12 +7813,16 @@ gtk_window_handle_wm_event (GtkWindow *window, { priv = window->priv; + if (run_drag && priv->drag_gesture) + retval |= gtk_event_controller_handle_event (GTK_EVENT_CONTROLLER (priv->drag_gesture), + (const GdkEvent*) event); + if (priv->multipress_gesture) - return gtk_event_controller_handle_event (GTK_EVENT_CONTROLLER (priv->multipress_gesture), - (const GdkEvent*) event); + retval |= gtk_event_controller_handle_event (GTK_EVENT_CONTROLLER (priv->multipress_gesture), + (const GdkEvent*) event); } - return GDK_EVENT_PROPAGATE; + return retval; } gboolean @@ -7792,6 +7833,9 @@ _gtk_window_check_handle_wm_event (GdkEvent *event) widget = gtk_get_event_widget (event); + if (!GTK_IS_WINDOW (widget)) + widget = gtk_widget_get_toplevel (widget); + if (!GTK_IS_WINDOW (widget)) return GDK_EVENT_PROPAGATE; @@ -7808,7 +7852,7 @@ _gtk_window_check_handle_wm_event (GdkEvent *event) if (gtk_widget_event (widget, event)) return GDK_EVENT_STOP; - return gtk_window_handle_wm_event (GTK_WINDOW (widget), event); + return gtk_window_handle_wm_event (GTK_WINDOW (widget), event, TRUE); } static gboolean @@ -7816,7 +7860,7 @@ gtk_window_event (GtkWidget *widget, GdkEvent *event) { if (widget != gtk_get_event_widget (event)) - return gtk_window_handle_wm_event (GTK_WINDOW (widget), event); + return gtk_window_handle_wm_event (GTK_WINDOW (widget), event, FALSE); return GDK_EVENT_PROPAGATE; } -- 2.30.2